package io.kiki.stack.netty.invoker;

import io.kiki.stack.netty.command.AbstractResponseCommand;
import io.kiki.stack.netty.command.ResponseCommand;
import io.kiki.stack.netty.command.ResponseType;
import io.kiki.stack.netty.exception.*;
import io.kiki.stack.netty.util.StringUtils;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;


@Slf4j
@Setter
@Getter
public class RpcResponseResolver {


    public static Object resolveResponseObject(ResponseCommand responseCommand, String addr) throws RemotingException {
        preProcess(responseCommand, addr);
        if (responseCommand.getResponseStatus() == ResponseType.success) {
            return toResponseObject(responseCommand);
        } else {
            String msg = String.format("Rpc invocation exception: %s, the address is %s, id=%s", responseCommand.getResponseStatus(), addr, responseCommand.getId());
            log.warn(msg);
            if (responseCommand.getCause() != null) {
                throw new InvokeException(msg, responseCommand.getCause());
            } else {
                throw new InvokeException(msg + ", please check the server log for more.");
            }
        }

    }

    private static void preProcess(ResponseCommand responseCommand, String addr) throws RemotingException {
        RemotingException e = null;
        String msg = null;
        if (responseCommand == null) {
            msg = String.format("Rpc invocation timeout[responseCommand null]! the address is %s", addr);
            e = new InvokeTimeoutException(msg);
        } else {
            switch (responseCommand.getResponseStatus()) {
            case timeout:
                msg = String.format("Rpc invocation timeout[responseCommand TIMEOUT]! the address is %s", addr);
                e = new InvokeTimeoutException(msg);
                break;
            case client_send_error:
                msg = String.format("Rpc invocation send failed! the address is %s", addr);
                e = new InvokeSendFailedException(msg, responseCommand.getCause());
                break;
            case channel_closed:
                msg = String.format("Channel closed! the address is %s", addr);
                e = new ChannelClosedException(msg);
                break;
            case server_threadpool_busy:
                msg = String.format("Server thread pool busy! the address is %s, id=%s", addr, responseCommand.getId());
                e = new InvokeServerBusyException(msg);
                break;
            case codec_exception:
                msg = String.format("Codec exception! the address is %s, id=%s", addr, responseCommand.getId());
                e = new CodecException(msg);
                break;
            case server_serial_exception:
                msg = String.format("Server serialize response exception! the address is %s, id=%s, serverSide=true", addr, responseCommand.getId());
                e = new SerializationException(detailErrMsg(msg, responseCommand), toThrowable(responseCommand), true);
                break;
            case server_deserial_exception:
                msg = String.format("Server deserialize request exception! the address is %s, id=%s, serverSide=true", addr, responseCommand.getId());
                e = new DeserializationException(detailErrMsg(msg, responseCommand), toThrowable(responseCommand), true);
                break;
            case server_exception:
                msg = String.format("Server exception! Please check the server log, the address is %s, id=%s", addr, responseCommand.getId());
                e = new InvokeServerException(detailErrMsg(msg, responseCommand), toThrowable(responseCommand));
                break;
            default:
                break;
            }
        }
        if (StringUtils.isNotBlank(msg)) {
            log.warn(msg);
        }
        if (null != e) {
            throw e;
        }
    }


    private static Object toResponseObject(ResponseCommand responseCommand) throws CodecException {
        AbstractResponseCommand response = (AbstractResponseCommand) responseCommand;
        response.deserialize();
        return response.getResponseObject();
    }


    private static Throwable toThrowable(ResponseCommand responseCommand) throws CodecException {
        AbstractResponseCommand responseCommand1 = (AbstractResponseCommand) responseCommand;
        responseCommand1.deserialize();
        Object ex = responseCommand1.getResponseObject();
        if (ex instanceof Throwable) {
            return (Throwable) ex;
        }
        return null;
    }

    /**
     * Detail your error msg with the error msg returned from response command
     */
    private static String detailErrMsg(String clientErrMsg, ResponseCommand responseCommand) {
        AbstractResponseCommand resp = (AbstractResponseCommand) responseCommand;
        if (StringUtils.isNotBlank(resp.getErrorMsg())) {
            return String.format("%s, ServerErrorMsg:%s", clientErrMsg, resp.getErrorMsg());
        } else {
            return String.format("%s, ServerErrorMsg:null", clientErrMsg);
        }
    }
}
